/**
 * @file Editor.cpp
 *
 * @date 17/12/2011
 * @author Tales 
 *
 */

#include "Editor.hpp"
#include "Tool.hpp"
#include "PencilTool.hpp"
#include "LinesTool.hpp"
#include "../model/Picture.hpp"
#include "FloodFillTool.hpp"
#include "AreaTool.hpp"
#include "SelectionTool.hpp"

#define SDL_CLIPBOARD_LIBRARY_STATIC
#include <SDL_Clipboard.h>
#define SDL_SURFACEUTILS_LIBRARY_STATIC
#include <SDL_SurfaceUtils.h>
#include "../model/RemoveLayerCommand.hpp"
#include "../model/AddLayerCommand.hpp"
#include "../model/ChangeLayerCommand.hpp"
#include "../Graphics.hpp"

namespace yep
{

const size_t N_TOOLS = 8;

//Tool **Editor::tools = NULL;
void Editor::init(pair<int, int> size)
{
	preview = NULL;
	picture = NULL;
	selected = NULL;
	tool = -1;
	tools = new Tool*[N_TOOLS];
	//inicializar os tools aqui:
	tools[0] = new PencilTool;
	tools[0]->setEditor(*this);
	tools[1] = new LinesTool;
	tools[1]->setEditor(*this);
	tools[2] = new AreaTool(RECT);
	tools[2]->setEditor(*this);
	tools[3] = new AreaTool(FILLED_RECT);
	tools[3]->setEditor(*this);
	tools[4] = new AreaTool(ELLIPSIS);
	tools[4]->setEditor(*this);
	tools[5] = new AreaTool(FILLED_ELLIPSIS);
	tools[5]->setEditor(*this);
	tools[6] = new FloodFillTool;
	tools[6]->setEditor(*this);
	tools[7] = new SelectionTool;
	tools[7]->setEditor(*this);
	newPicure(size);
	setTool(0);
}

Editor::Editor(pair<int, int> size) :
		color(Color::BLACK)
{
	init(size);
}

Editor::Editor() : //todo: quando botar as mãos em um compilador que suporte isso, delegar a construção disso para o outro construtor.
		color(Color::BLACK)
{
	init(make_pair(800, 600));
}

Editor::~Editor()
{
	if (preview)
		SDL_FreeSurface(preview);

}

void Editor::newPicure(pair<int, int> size)
{
	if (preview)
		SDL_FreeSurface(preview);

	if (picture)
		delete picture;

	picture = new Picture(size.first, size.second, 24);
	currentLayer = 0;
	preview = SDL_CreateRGBSurface(SDL_SWSURFACE, size.first, size.second, 32, 0x00ff0000, 0x0000ff00, 0x000000ff,
			0xff000000);
}

bool Editor::openPicture(string file)
{
	bool imported;
	if (file.find(".bmp") != string::npos)
		imported = true;
	else if (file.find(".lbmp") != string::npos)
		imported = false;
	else
		return false;
	SDL_RWops *input = SDL_RWFromFile(file.c_str(), "rb");
	if (!input)
		return false;
	Picture *nPic = new Picture(input, imported);
	SDL_RWclose(input);
	if (nPic)
	{
		if (preview)
			SDL_FreeSurface(preview);

		if (picture)
			delete picture;
		picture = nPic;
		currentLayer = 0;
		preview = SDL_CreateRGBSurface(SDL_SWSURFACE, nPic->getW(), nPic->getH(), 32, 0x00ff0000, 0x0000ff00,
				0x000000ff, 0xff000000);
		return true;
	}
	return false;
}

bool Editor::savePicture(string file)
{
	bool exported;
	if (file.find(".bmp") != string::npos)
		exported = true;
	else if (file.find(".lbmp") != string::npos)
		exported = false;
	else
		return false;
	SDL_RWops *output = SDL_RWFromFile(file.c_str(), "wb");
	if (!output)
		return false;
	picture->save(output, exported);
	SDL_RWclose(output);
	return true;
}

void Editor::render(SDL_Surface *dst)
{
	SDL_Rect rect = {0,0, (Sint16)preview->w, (Sint16)preview->h};
	SDL_FillRect(dst, &rect, 0xffFFffFF);
	picture->render(dst);
	SDL_BlitSurface(preview, NULL, dst, NULL);
}

void Editor::addLayer(int pos)
{
	AddLayerCommand *command = new AddLayerCommand(pos);
	picture->addCommand(command);
}

void Editor::removeLayer(int layer)
{
	if (layer < 0)
		layer = currentLayer;

	RemoveLayerCommand *command = new RemoveLayerCommand(layer);
	picture->addCommand(command);
}
void Editor::cloneLayer(int newPos)
{
	AddLayerCommand *command = new AddLayerCommand(newPos, picture->getCopy(currentLayer));
	picture->addCommand(command);
}

void Editor::changePosLayer(int other)
{
	ChangeLayerCommand *command = new ChangeLayerCommand(currentLayer, other, false);
	picture->addCommand(command);
}

void Editor::merge(bool eraseOthers)
{
	SDL_Surface *merged = Graphics(preview).clone(); // TODO: Resolver problema de resolução (a camada mergeada pode ser diferente das outras)
	SDL_FillRect(merged, NULL, 0);
	picture->render(merged);
	AddLayerCommand *command = new AddLayerCommand(0, merged);
	picture->addCommand(command);
	if (eraseOthers)
		picture->addCommand(new RemoveLayerCommand(-1, picture->getLayerCount() - 1));

}

size_t Editor::getLayerCount()
{
	return picture->getLayerCount();
}

size_t Editor::getToolCount()
{
	return N_TOOLS;
}

string Editor::getToolName(size_t index)
{
	static const char *toolNames[N_TOOLS] = { "Lápis", "Retas", "Retangulo", "Retangulo Preenchido", "Elípse", "Elípse Preenchida", "Flood Fill", "Seleção"};
	YEP_ERROR_TEST(index < N_TOOLS, "Invalid Tool Index");
	return toolNames[index];
}

int Editor::getCurrentLayer() const
{
	return currentLayer;
}

size_t Editor::getTool() const
{
	return tool;
}

void Editor::setCurrentLayer(int currentLayer)
{
	this->currentLayer = currentLayer;
}

void Editor::pressed(pair<int, int> pos, bool leftButton, bitset<5> state)
{
	YEP_ASSERT(getToolCount()>0);
	tools[tool]->pressed(pos, leftButton, state);
}

void Editor::released(pair<int, int> pos, bool leftButton, bitset<5> state)
{
	YEP_ASSERT(getToolCount()>0);
	tools[tool]->released(pos, leftButton, state);
}

void Editor::moved(pair<int, int> pos, pair<int, int> offset, std::bitset<5> state)
{
	YEP_ASSERT(getToolCount()>0);
	tools[tool]->moved(pos, offset, state);
}

void Editor::undo()
{
	picture->undo();
}

void Editor::redo()
{
	picture->redo();
}

void Editor::setTool(size_t tool)
{
	if (this->tool >= 0)
		tools[this->tool]->end();
	this->tool = tool;
	tools[tool]->start();
}

void Editor::selectFromRect(const SDL_Rect & rect)
{
	SDL_FreeSurface(selected);
	selected = picture->getCopy(currentLayer, rect);
}

bool Editor::selectFromClipboard()
{
	char *dst = NULL;
	size_t len = 0;
	SDLScrap_PasteFromClipboard(SDL_CLIPBOARD_IMAGE_TYPE, &len, &dst);
	if (len > 0)
	{
		SDL_FreeSurface(selected);
		selected = SDL_ConvertBMPBufferToSurface(len, dst);
		SDLScrap_FreeBuffer(dst);
		tool = 7;
		SelectionTool &selectionTool = dynamic_cast<SelectionTool &>(*tools[7]);
		selectionTool.notifyPaste();
		return true;
	}
	return false;
}

void Editor::selectToClipboard()
{
	if (selected != NULL)
	{
		size_t len = 0;
		char *buffer = SDL_ConvertSurfaceToBMPBuffer(selected, &len);
		SDLScrap_CopyToClipboard(SDL_CLIPBOARD_IMAGE_TYPE, len, buffer);
		SDL_FreeBMPBuffer(buffer);
	}
}

void Editor::unselect()
{
	SDL_FreeSurface(selected);
	selected = NULL;
}

} /* namespace yep */
